namespace Universe.MediaProbe
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;

    using External;

    public class ProbeReportBuilder
    {
        public static bool RemoveBinariesOnUnload = true;
        public static Implementation[] Preferred = {Implementation.FF, Implementation.AV,};
        private static Implementation Current = Implementation.AV;
        
        public enum Implementation
        {
            FF,
            AV,
        }

        public class ImplementationInfo
        {
            public Implementation Kind { get; set; }
            public bool IsPresent { get; set; }
            public string Version { get; set; }

            public override string ToString()
            {
                return string.Format("{{{0}-Probe: {1}}}", Kind, !IsPresent ? "Absent" : Version);
            }
        }

        public static ProbeReport BuildProbe(string fileName)
        {
            var candidates = GetCandidates();
            Exception exception = null;
            foreach (var implementation in candidates)
            {
                try
                {
                    var ret = GetInstance(implementation).BuildProbe(fileName);
                    Current = implementation;
                    return ret;
                }
                catch (Exception ex)
                {
                    exception = exception ?? ex;
                }
            }

            var msg = "Both avprobe and ffprobe are unable to build report. Check they are installed.";
            if (CrossInfo.ThePlatform == CrossInfo.Platform.Windows)
                msg = "ffprobe.exe is unable to build report.";

            if (exception != null)
                throw new InvalidOperationException(msg, exception);
            else
                throw new InvalidOperationException(msg);
        }

        public static Implementation[] GetCandidates()
        {
            var candidates = new[] {Implementation.AV, Implementation.FF,};
            if (Current == Implementation.FF) Array.Reverse(candidates);
            if (CrossInfo.ThePlatform == CrossInfo.Platform.Windows) candidates = new[] {Implementation.FF,};
            return candidates;
        }

        public static byte[] BuildRawProbe(string fileName)
        {
            var candidates = GetCandidates();
            Exception exception = null;
            foreach (var implementation in candidates)
            {
                try
                {
                    var ret = GetInstance(implementation).BuildRawProbe(fileName);
                    Current = implementation;
                    return ret;
                }
                catch (Exception ex)
                {
                    exception = exception ?? ex;
                }
            }

            var msg = "Both avprobe and ffprobe are unable to build report. Check they are installed.";
            if (CrossInfo.ThePlatform == CrossInfo.Platform.Windows)
                msg = "ffprobe.exe is unable to build report.";

            if (exception != null)
                throw new InvalidOperationException(msg, exception);
            else
                throw new InvalidOperationException(msg);
        }

        static IProbeReportBuilder GetInstance(Implementation kind)
        {
            return kind == Implementation.AV ? (IProbeReportBuilder) new ProbeReportBuilder_AV() : new ProbeReportBuilder_FF();
        }

        public static ImplementationInfo GetImplementationInfo(Implementation kind)
        {
            string firstLine = null;
            string exe = kind == Implementation.AV ? "avprobe" : ExternalSource.GetFFProbeFile();
            try
            {
                string output;
                int code;
                CrossInfo.HiddenExec(exe, "-version", out output, out code);
                if (output != null)
                {
                    var arr = output.Split('\r', '\n');
                    foreach (var s in arr)
                    {
                        if (!string.IsNullOrEmpty(s))
                        {
                            firstLine = s;
                            break;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Trace.WriteLine("Version of '" + exe + "' failed." + Environment.NewLine + ex);
            }

            var isPresent = !string.IsNullOrEmpty(firstLine);
            string required = kind == Implementation.AV ? "avprobe" : "ffprobe";
            if (isPresent && firstLine.IndexOf(required, StringComparison.OrdinalIgnoreCase) < 0)
                isPresent = false;

            return new ImplementationInfo()
            {
                Kind = kind,
                IsPresent = isPresent,
                Version = isPresent ? firstLine : null,
            };
        }

        
    }
}